#define GLEW_STATIC

#include "Graphic.h"
#include "../Log/Log.h"
#include "../World/World.h"
#include "../System/iniparser.h"

int WINDOW_WIDTH   =  1280;
int WINDOW_HEIGHT  =  800;
int GRAPHIC_DEPTHS = -200;

/* SDL */
SDL_Window *Window;
SDL_Renderer *Screen;
SDL_Event Event;
SDL_TimerID G_Timer;

G_Input Input;
G_Rect Camera;
G_Mouse Mouse;
G_Framenrate Framenrate;
G_TimerCount TimerCount;

int Graphic_LoadConfig()
{
    dictionary  *   ini ;

    ini = iniparser_load("Config.ini");
    if (ini==NULL) {
        logError("cannot parse file");
        return -1 ;
    }

    /* Lade die Werte */
    WINDOW_WIDTH = iniparser_getint(ini, "graphic:width", 1280);
    WINDOW_HEIGHT = iniparser_getint(ini, "graphic:height", 800);

    /* Schließen */
    iniparser_freedict(ini);
    return 0 ;
}

/** Starte SDL */
int Graphic_Init()
{
    /* SDL init */
    SDL_Init(SDL_INIT_EVERYTHING);

    /* Timer  */
    G_Timer = SDL_AddTimer (250, (void *)Graphic_Timer, NULL);

    /* Einstlungen laden */
    Graphic_LoadConfig();

    /* Erstelle ein Fenster */
    Window = SDL_CreateWindow("Pyrus",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              WINDOW_WIDTH, WINDOW_HEIGHT,
                              SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
    if (!Window) {
        logError("Window not initialized: %s", SDL_GetError());
        return 0;
    } else {
        /* Create context */
        Screen = SDL_GL_CreateContext( Window );
        if( Screen == NULL )
        {
            logError( "OpenGL context could not be created! SDL Error: %s", SDL_GetError() );
            return 0;
        }
        else
        {
            /* Sync mit Bildschirm */
            if( SDL_GL_SetSwapInterval( 1 ) < 0 )
            {
                logWarning( "Unable to set VSync! SDL Error: %s", SDL_GetError() );
            }
            /* Initialize OpenGL */
            if( !Graphic_InitGL() )
            {
                logError( "Unable to initialize OpenGL!" );
                return 0;
            }
        }
    }

    /* Lade die Daten */
    if(Graphic_LoadFiles() == 0)
    {
        logError("Unable to load the Graphic Files!");
        return 0;
    }

    glewInit();

    /* was unterstützt wird */
    logDebug("OpenGL version supported by this platform (%s):", glGetString(GL_VERSION));

    /* Ob die anfoderungen stimmen */
    if (!GLEW_VERSION_2_0) {
        logError("OpenGL 2.0 not available, but we need it");
        return 0;
    }


    /* Camera */
    Camera.X = 0;
    Camera.Y = 0;
    Camera.W = WINDOW_WIDTH;
    Camera.H = WINDOW_HEIGHT;
    Camera.MaxW = WINDOW_WIDTH;
    Camera.MaxH = WINDOW_HEIGHT;

    return 1;
}

/* OpenGL zusammenkratzen */
int Graphic_InitGL()
{
    /* Viewport und die 2D Ansicht einstellen */
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GRAPHIC_DEPTHS, 0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    /* Einstellungen 2D */
    glDisable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA_TEST);
    glAlphaFunc(GL_GREATER, 0.1);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	/* Alte Datei */
	glLoadIdentity();

    if(glGetError() != GL_NO_ERROR)
    {
        logError("glGetError %d\n ", glGetError());
        return 0;
    }

	return 1;
}

/** Image laden */
G_TextureImage Graphic_LoadImage(char *filename)
{
    int w, h;
    G_TextureImage Tex;
    GLuint tex;

    /* Pixeldaten speicher einstellen */
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	/* Textur erstellen */
	glGenTextures(1, &tex);
	glBindTexture(GL_TEXTURE_2D, tex);

	/* Textur Pixelgenau machen */
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	/* Datei öffnen und lesen */
	GLubyte *pixels = (GLubyte *)load_png(filename, &w, &h);
	/* Als textur(opengl) speichern */
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
	free(pixels);

	/* Als structur speichern */
	Tex.height = h;
	Tex.width = w;
	Tex.imageData = (GLuint *)tex;

	return Tex;
}

/* Berechnungen die ihre zeit brauchen wie effekte oder bewegungen */
int Graphic_Timer(Uint32 intervall, void *parameter)
{
    TimerCount.Count += 1;
    return intervall;
}


/** Camera */
int Graphic_GetCamera( int XYorWH)
{
    switch(XYorWH) {
        case 0: return Camera.X; break;
        case 1: return Camera.Y; break;
        case 2: return Camera.W; break;
        case 3: return Camera.H; break;
        default: return 0;
    }
}

void Graphic_Camera( int X, int Y)
{
    Camera.X += X;
    Camera.Y += Y;

    if(Camera.X <= 0)
        Camera.X = 0;
    if(Camera.Y <= 0)
        Camera.Y = 0;

    if(Camera.X >= Camera.MaxW-Camera.W)
        Camera.X = Camera.MaxW-Camera.W;
    if(Camera.Y >= Camera.MaxH-Camera.H)
        Camera.Y = Camera.MaxH-Camera.H;
}

void Graphic_SetCamera(int maxW, int maxH)
{
    Camera.MaxW = maxW;
    Camera.MaxH = maxH;
    if(Camera.MaxW < Camera.W)
        Camera.MaxW = Camera.W;
    if(Camera.MaxH < Camera.H)
        Camera.MaxH = Camera.H;
}

void Graphic_CentralCamera(int W, int H)
{
    Camera.X = W*30-(Camera.W/2);
    Camera.Y = H*15-(Camera.H/2);
    if(Camera.Y < 0)
        Camera.Y = 0;
    if(Camera.X < 0)
        Camera.X = 0;
}

int Graphic_Process()
{
    /* Jetzige zeit */
    Framenrate.currentTime = SDL_GetTicks();
    if ( Framenrate.currentTime - Framenrate.past >= 16 ) {
        Framenrate.past = SDL_GetTicks();
        Framenrate.FPS++;
    }

    /* Check for events */
    while (SDL_PollEvent(&Event)) {
        /* Mouse */
        SDL_GetMouseState((int *)&Mouse.X, (int *)&Mouse.Y);
        Input.X = Mouse.X;
        Input.Y = Mouse.Y;

        if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(1)) {
            Input.Mouse_1 = 1;
        }

        if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(2)) {
            Input.Mouse_2 = 1;
        }

        if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(3)) {
            Input.Mouse_3 = 1;
        }

        switch (Event.type)
        {
            case SDL_QUIT: return 0; break;
            case SDL_KEYDOWN:
                switch( Event.key.keysym.sym )
                {
                    case SDLK_UP:       Graphic_Camera(0,-100); break;
                    case SDLK_DOWN:     Graphic_Camera(0,100); break;
                    case SDLK_RIGHT:    Graphic_Camera(100,0); break;
                    case SDLK_LEFT:     Graphic_Camera(-100,0); break;
                }
            break;
        }
    }

    /* Auf den Bildschirm */
    SDL_GL_SwapWindow( Window );
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	/* Alte Datei */
	glLoadIdentity();

    /* Danach */
    if ( Framenrate.currentTime - Framenrate.pastFPS >= 1000 )
    {
        static char buffer[20] = {0};
        sprintf( buffer, "%d FPS", Framenrate.FPS );
        SDL_SetWindowTitle( Window,buffer);

        Framenrate.FPS = 0;
        Framenrate.pastFPS = Framenrate.currentTime;
    }

    /* Überlauf schutz */
    if( TimerCount.Count > 100 )
        TimerCount.Count -= 100;
    return 1;
}

void Graphic_ApplySurface(int X, int Y, int W, int H, GLuint *tex, int Z)
{
    glPushMatrix();

    /* Achsen setzten */
    glTranslatef( X, Y, 0);

    /* Enable the texture for OpenGL. */
    glEnable(GL_TEXTURE_2D);
    glBindTexture (GL_TEXTURE_2D, (int)tex);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    /* Textur */
	glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0); glVertex3f(0, H, Z);
        glTexCoord2f(0.0, 1.0); glVertex3f(0, 0, Z);
        glTexCoord2f(1.0, 1.0); glVertex3f(W, 0, Z);
        glTexCoord2f(1.0, 0.0); glVertex3f(W, H, Z);
	glEnd();

	glPopMatrix();
}

/**                 */
/** Game inhalte    */
/**                 */
G_TextureImage Tile[128];
G_TextureImage Human[15];
G_TextureImage Circle;
G_TextureImage Circleblack;

SDL_Rect rHuman[16];

/* Laden der Daten */
int Graphic_LoadFiles()
{
    char sFile[256];
    int i;

    /* Human */
    for( i = 0; i < 3; i++) {
        sprintf (sFile, "GFX/human/human_e%d.png", i+1);
        Human[i] = Graphic_LoadImage(sFile);
    }
    for( i = 0; i < 3; i++) {
        sprintf (sFile, "GFX/human/human_w%d.png", i+1);
        Human[i+3] = Graphic_LoadImage(sFile);
    }
    for( i = 0; i < 3; i++) {
        sprintf (sFile, "GFX/human/human_n%d.png", i+1);
        Human[i+6] = Graphic_LoadImage(sFile);
    }
    for( i = 0; i < 3; i++) {
        sprintf (sFile, "GFX/human/human_s%d.png", i+1);
        Human[i+9] = Graphic_LoadImage(sFile);
    }

    Circle = Graphic_LoadImage("GFX/circle.png");
    Circleblack = Graphic_LoadImage("GFX/circleblack.png");

    /* Tiles */
    Tile[1] = Graphic_LoadImage("GFX/basic1.png");
    Tile[2] = Graphic_LoadImage("GFX/ground.png");
    Tile[3] = Graphic_LoadImage("GFX/highlighted.png");

    /* Street */
    Tile[4] = Graphic_LoadImage("GFX/path/path_s.png"); //
    Tile[5] = Graphic_LoadImage("GFX/path/path.png"); //
    Tile[6] = Graphic_LoadImage("GFX/path/path_new.png"); //
    Tile[7] = Graphic_LoadImage("GFX/path/path_n.png"); //
    Tile[8] = Graphic_LoadImage("GFX/path/path_e.png"); //
    Tile[9] = Graphic_LoadImage("GFX/path/path_w.png"); //
    Tile[10] = Graphic_LoadImage("GFX/path/path_ew.png"); //
    Tile[11] = Graphic_LoadImage("GFX/path/path_ns.png"); //
    Tile[12] = Graphic_LoadImage("GFX/path/path_nse.png");
    Tile[13] = Graphic_LoadImage("GFX/path/path_nsew.png");
    Tile[14] = Graphic_LoadImage("GFX/path/path_nw.png"); //
    Tile[15] = Graphic_LoadImage("GFX/path/path_se.png"); //
    Tile[16] = Graphic_LoadImage("GFX/path/path_sew.png");
    Tile[17] = Graphic_LoadImage("GFX/path/path_sw.png");//
    Tile[18] = Graphic_LoadImage("GFX/path/path_ne.png");//
    Tile[19] = Graphic_LoadImage("GFX/path/path_nsw.png");

    /* Bäume */
    for( i = 0; i < 18; i++)
    {
        sprintf (sFile, "GFX/trees/trees%d.png", i+1);
        Tile[20+i] = Graphic_LoadImage(sFile);
    }

    /* Häuser */
    Tile[60] = Graphic_LoadImage("GFX/buildings/house1.png");
    Tile[61] = Graphic_LoadImage("GFX/buildings/house2.png");
    Tile[62] = Graphic_LoadImage("GFX/buildings/stonework.png");
    Tile[63] = Graphic_LoadImage("GFX/buildings/woodcutter.png");
    Tile[90] = Graphic_LoadImage("GFX/buildings/encampment.png");
    Tile[80] = Graphic_LoadImage("GFX/buildings/warehouse.png");

    /* Erze */
    for( i = 0; i < 1; i++)
    {
        sprintf (sFile, "GFX/mine%d.png", i+1);
        Tile[100+i] = Graphic_LoadImage(sFile);
    }

    /* Icons */
    Tile[120] = Graphic_LoadImage("GFX/icons/build.png");

    return 1;
}

void Graphic_Tile(int X, int Y, int Data) {
    int rX, rY, rZ;
    rX = X*30 - Y*30;
    rY = (X*30 + Y*30) / 2;
    rX -= Camera.X - (WorldWidth*30);
    rY -= Camera.Y;
    rZ = Y+X;

    if(rX < 0-Tile[Data].width  || rY < 0-Tile[Data].height)
        return;
    if(rX > Camera.W+Tile[Data].width || rY > Camera.H+Tile[Data].height)
        return;

    if(Data == Highlighted_Tile)
        rZ+=1;

    /* Blit */
    Graphic_ApplySurface( rX-(Tile[Data].width/2), rY-(Tile[Data].height/2), Tile[Data].width, Tile[Data].height, Tile[Data].imageData, rZ);
}

int Graphic_Human(int X, int Y, int AddZ, int iAnimation, int Time) {
    int rX, rY, rZ, iW, iH;

    rX = X;
    rY = Y;
    rX -= Camera.X;
    rY -= Camera.Y;

    if(rX < 0-100  || rY < 0-100)
        return TimerCount.Count;
    if(rX > Camera.W || rY > Camera.H)
        return TimerCount.Count;

    rZ = XIso(X,Y)+YIso(X,Y)+1+AddZ;

    iW = Human[iAnimation+Time].width;
    iH = Human[iAnimation+Time].height;

    /* Blit */
    Graphic_ApplySurface(rX-iW/2,
                         rY,
                         iW,
                         iH,
                         Human[iAnimation+Time].imageData,
                         rZ);

    return TimerCount.Count;
}

void Graphic_Circle(int X, int Y, float R, int Style)
{
    int rX,rY;
    float rW,rH;

    G_TextureImage *pCircle;

    rX = X;
    rY = Y;
    //rX -= Camera.X;
    //rY -= Camera.Y;
    rW = ((float)Circle.width-90)*R;
    rH = ((float)Circle.height-90)*R;

    switch(Style) {
        case 1:
            pCircle = &Circleblack;
        break;
        default:
            pCircle = &Circle;
        break;
    }

    /* Blit */
    Graphic_ApplySurface((int)rX-rW/2,
                         (int)rY-rH/2,
                         (int)rW,
                         (int)rH,
                         pCircle->imageData,
                         -GRAPHIC_DEPTHS-21);
    /*Graphic_ApplySurface(rX,
                         rY,
                         10,
                         10,
                         Circle.imageData,
                         -GRAPHIC_DEPTHS-20);*/
}

void Graphic_BuildIcon(int X, int Y, float Zoom, char *Title)
{
    float rX = X;
    float rY = Y;
    int rW = 64*Zoom;
    int rH = 100*Zoom;
    int ID = MenuLogic_BuildID(Title);

    /* Blit */
    Graphic_ApplySurface((int)rX-rW/2,
                         (int)rY-rH/1.5,
                         rW,
                         rH,
                         Tile[ID].imageData,
                         -GRAPHIC_DEPTHS-20);
}

void Graphic_Test(int X, int Y, float Zoom, char *Title)
{
    float rX = X;
    float rY = Y;
    int rW = 64*Zoom;
    int rH = 100*Zoom;
    /* Blit */
    Graphic_ApplySurface((int)rX-rW/2,
                         (int)rY-rH/1.5,
                         rW,
                         rH,
                         Tile[63].imageData,
                         -GRAPHIC_DEPTHS-20);
}

void Graphic_Quit()
{
    SDL_GL_DeleteContext(Screen);
    SDL_DestroyWindow(Window);
    SDL_Quit();
}
